import sys
from devleo.utils.common import get_absolute_resource_path
import requests
from bs4 import BeautifulSoup
from PyQt5.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel,
    QLineEdit, QPushButton, QTextEdit, QGroupBox, QTimeEdit,
    QSystemTrayIcon, QMenu, QAction, QStyle, QRadioButton
)
from PyQt5.QtCore import QTimer, Qt, QThread, pyqtSignal, QTime, QSharedMemory
from PyQt5.QtNetwork import QLocalSocket, QLocalServer
from PyQt5.QtGui import QIcon, QCloseEvent
import datetime
import os

# 导入配置管理模块
from devleo.config import Config


class IPSWChecker(QThread):
    # 定义信号，用于向主线程发送结果
    result_signal = pyqtSignal(str, str)  # 新增参数用于传递版本信息
    error_signal = pyqtSignal(str)

    def __init__(self, url, search_value):
        super().__init__()
        self.url = url
        self.search_value = search_value
        self.is_running = True
        self.found_version = None  # 用于存储找到的版本信息

    def run(self):
        try:
            self.check_ipsw()
        except Exception as e:
            self.error_signal.emit(f"检查过程中出现错误: {str(e)}")

    def check_ipsw(self):
        try:
            resp = requests.get(self.url, timeout=10)
            resp.encoding = 'utf-8'

            # 使用BeautifulSoup解析HTML
            soup = BeautifulSoup(resp.text, 'html.parser')

            # 查找包含"Signed IPSWs"的h4标签
            signed_ipsws_header = soup.find('h4', string='Signed IPSWs')

            if signed_ipsws_header:
                # 找到紧随其后的table元素
                table = signed_ipsws_header.find_next('table')

                if table:
                    # 查找所有行（tr元素）
                    rows = table.find_all('tr', class_='firmware')

                    # 检查每行的第二列（td）是否包含搜索值
                    contains_value = False
                    for row in rows:
                        columns = row.find_all('td')
                        if len(columns) >= 2:  # 确保至少有两列
                            second_column_text = columns[1].get_text(strip=True)
                            if self.search_value in second_column_text:
                                self.found_version = second_column_text  # 保存找到的版本信息
                                contains_value = True
                                msg = f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 找到匹配项: {second_column_text}"
                                self.result_signal.emit(msg, second_column_text)
                                break

                    if not contains_value:
                        msg = f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 未找到包含'{self.search_value}'的版本"
                        self.result_signal.emit(msg, "")
                else:
                    self.error_signal.emit("未找到表格")
            else:
                self.error_signal.emit("未找到'Signed IPSWs'标题")
        except Exception as e:
            self.error_signal.emit(f"网络请求失败: {str(e)}")

    def stop(self):
        self.is_running = False


class IPSWMonitorGUI(QWidget):
    def __init__(self):
        super().__init__()
        self.checker_thread = None
        self.timer = QTimer()
        self.timer.timeout.connect(self.start_check)
        self.tray_icon = None
        self.server = None
        self.config = Config.get_instance()  # 获取配置实例
        self.init_ui()
        self.load_config()  # 加载配置
        self.create_tray_icon()
        self.setup_local_server()
        # 设置初始状态
        self.toggle_dingtalk_config()

    def init_ui(self):
        self.setWindowTitle('IPSW Monitor')
        self.setWindowIcon(QIcon(get_absolute_resource_path("icon.ico")))
        self.setGeometry(100, 100, 800, 600)
        # 获取屏幕几何信息并将窗口居中
        screen_geometry = QApplication.desktop().screenGeometry()
        window_geometry = self.frameGeometry()
        window_geometry.moveCenter(screen_geometry.center())
        self.move(window_geometry.topLeft())

        # 主布局
        main_layout = QVBoxLayout()

        # URL 输入组
        url_group = QGroupBox("目标URL(默认iPhone 14 Pro.可根据机型修改,网址: https://ipsw.me/)")
        url_layout = QHBoxLayout()
        self.url_label = QLabel("URL:")
        self.url_input = QLineEdit()
        self.url_input.setText("https://ipsw.me/iPhone15,2")
        url_layout.addWidget(self.url_label)
        url_layout.addWidget(self.url_input)
        url_group.setLayout(url_layout)
        main_layout.addWidget(url_group)

        # 搜索参数组
        search_group = QGroupBox("参数设置")
        search_layout = QHBoxLayout()
        self.search_label = QLabel("版本关键词:")
        self.search_input = QLineEdit()
        self.search_input.setText("18")
        search_layout.addWidget(self.search_label)
        search_layout.addWidget(self.search_input)
        
        # 固定监控时间设置
        self.time_label = QLabel("监控时间(每天执行):")
        self.time_input = QTimeEdit()
        self.time_input.setTime(QTime(9, 0))  # 默认早上9点
        self.time_input.setDisplayFormat("HH:mm")
        search_layout.addWidget(self.time_label)
        search_layout.addWidget(self.time_input)
        
        search_group.setLayout(search_layout)
        main_layout.addWidget(search_group)

        # 通知设置组
        notification_group = QGroupBox("通知设置")
        notification_layout = QVBoxLayout()
        
        # 通知类型选择
        notification_type_layout = QHBoxLayout()
        self.notification_type_label = QLabel("通知类型:")
        self.windows_notification_radio = QRadioButton("Windows系统通知")
        self.windows_notification_radio.setChecked(True)  # 默认选择Windows系统通知
        self.dingtalk_notification_radio = QRadioButton("钉钉通知")
        self.none_notification_radio = QRadioButton("不发送通知")
        
        notification_type_layout.addWidget(self.notification_type_label)
        notification_type_layout.addWidget(self.windows_notification_radio)
        notification_type_layout.addWidget(self.dingtalk_notification_radio)
        notification_type_layout.addWidget(self.none_notification_radio)
        notification_type_layout.addStretch()  # 添加弹性空间
        notification_layout.addLayout(notification_type_layout)
        
        # 钉钉配置
        dingtalk_config_layout = QHBoxLayout()
        self.dingtalk_webhook_label = QLabel("Webhook:")
        self.dingtalk_webhook_input = QLineEdit()
        # 不再设置默认值
        
        dingtalk_config_layout.addWidget(self.dingtalk_webhook_label)
        dingtalk_config_layout.addWidget(self.dingtalk_webhook_input)
        notification_layout.addLayout(dingtalk_config_layout)
        
        dingtalk_secret_layout = QHBoxLayout()
        self.dingtalk_secret_label = QLabel("Secret:")
        self.dingtalk_secret_input = QLineEdit()
        # 不再设置默认值
        
        dingtalk_secret_layout.addWidget(self.dingtalk_secret_label)
        dingtalk_secret_layout.addWidget(self.dingtalk_secret_input)
        notification_layout.addLayout(dingtalk_secret_layout)  # 添加这一行确保Secret配置显示
        notification_group.setLayout(notification_layout)
        main_layout.addWidget(notification_group)

        # 控制按钮组
        button_layout = QHBoxLayout()
        self.start_button = QPushButton("开始监控")
        self.stop_button = QPushButton("停止监控")
        self.stop_button.setEnabled(False)
        button_layout.addWidget(self.start_button)
        button_layout.addWidget(self.stop_button)
        main_layout.addLayout(button_layout)

        # 日志显示区域
        log_group = QGroupBox("运行日志")
        log_layout = QVBoxLayout()
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        
        # 添加清空日志按钮
        clear_log_layout = QHBoxLayout()
        clear_log_layout.addWidget(self.log_text)
        self.clear_log_button = QPushButton("清空日志")
        self.clear_log_button.clicked.connect(self.clear_log)
        clear_log_layout.addWidget(self.clear_log_button)
        
        log_layout.addLayout(clear_log_layout)
        log_group.setLayout(log_layout)
        main_layout.addWidget(log_group)

        # 连接信号和槽
        self.start_button.clicked.connect(self.start_monitoring)
        self.stop_button.clicked.connect(self.stop_monitoring)
        # 连接通知类型单选按钮
        self.windows_notification_radio.toggled.connect(self.toggle_dingtalk_config)
        self.dingtalk_notification_radio.toggled.connect(self.toggle_dingtalk_config)
        self.none_notification_radio.toggled.connect(self.toggle_dingtalk_config)

        self.setLayout(main_layout)

    def create_tray_icon(self):
        if not QSystemTrayIcon.isSystemTrayAvailable():
            return

        # 创建系统托盘图标
        self.tray_icon = QSystemTrayIcon(self)
        
        # 设置托盘图标提示信息
        self.tray_icon.setToolTip("IPSW Monitor\n左键点击显示主窗口\n右键点击显示菜单")
        
        # 设置图标（如果存在图标文件则使用，否则使用默认图标）
        from devleo.utils.common import get_absolute_resource_path
        icon_path = get_absolute_resource_path("icon.ico")  # 假设图标文件在当前目录
        if os.path.exists(icon_path):
            self.tray_icon.setIcon(QIcon(icon_path))
        else:
            # 如果没有找到图标文件，使用默认应用程序图标
            self.tray_icon.setIcon(self.style().standardIcon(QStyle.SP_ComputerIcon))

        # 创建托盘菜单
        tray_menu = QMenu()
        
        # 显示/隐藏操作
        show_action = QAction("显示", self)
        show_action.triggered.connect(self.show_window)
        tray_menu.addAction(show_action)
        
        # 退出操作
        quit_action = QAction("退出", self)
        quit_action.triggered.connect(self.exit_application)
        tray_menu.addAction(quit_action)
        
        self.tray_icon.setContextMenu(tray_menu)
        
        # 连接托盘图标激活信号
        self.tray_icon.activated.connect(self.on_tray_icon_activated)
        
        # 连接消息点击信号，实现点击通知后显示窗口并打开URL
        self.tray_icon.messageClicked.connect(self.on_message_clicked)
        
        # 显示托盘图标
        self.tray_icon.show()

    def on_tray_icon_activated(self, reason):
        """处理托盘图标点击事件"""
        if reason == QSystemTrayIcon.Trigger:
            # 单击托盘图标时显示窗口
            self.show_window()

    def show_window(self):
        """显示主窗口"""
        self.show()
        self.raise_()
        self.activateWindow()

    def exit_application(self):
        """退出应用程序"""
        # 停止定时器和线程
        self.stop_monitoring()
        if self.checker_thread and self.checker_thread.isRunning():
            self.checker_thread.quit()
            self.checker_thread.wait()
        
        # 隐藏托盘图标
        if self.tray_icon:
            self.tray_icon.hide()
        
        # 退出应用程序
        QApplication.quit()

    def closeEvent(self, event: QCloseEvent):
        """重写关闭事件，使窗口最小化到托盘而不是退出"""
        if self.tray_icon and self.tray_icon.isVisible():
            self.hide()
            event.ignore()
            self.log_message("程序已最小化到系统托盘，可以通过托盘图标退出程序")
        else:
            self.exit_application()
            event.accept()

    def on_message_clicked(self):
        """处理通知消息点击事件"""
        # 显示主窗口
        self.show_window()
        
        # 如果有保存的URL，则在默认浏览器中打开
        if hasattr(self, 'current_check_url') and self.current_check_url:
            import webbrowser
            webbrowser.open(self.current_check_url)
            self.log_message(f"已在浏览器中打开URL: {self.current_check_url}")

    def setup_local_server(self):
        """设置本地服务器用于单实例检查"""
        self.server = QLocalServer(self)
        self.server.newConnection.connect(self.handle_new_connection)
        
        # 移除可能存在的旧服务器
        QLocalServer.removeServer("IPSWMonitorUniqueID")
        
        # 启动监听
        if not self.server.listen("IPSWMonitorUniqueID"):
            self.log_message("无法启动本地服务器: " + self.server.errorString())

    def handle_new_connection(self):
        """处理新的本地连接（用于单实例检查）"""
        socket = self.server.nextPendingConnection()
        if socket:
            socket.readyRead.connect(lambda: self.read_socket_data(socket))

    def read_socket_data(self, socket):
        """读取套接字数据并处理指令"""
        if socket.bytesAvailable() > 0:
            data = socket.readAll().data()
            if data == b"EXIT":
                # 收到退出指令，关闭当前实例
                self.exit_application()
            elif data == b"SHOW":
                # 收到显示窗口指令，显示当前实例
                self.show_window()

    def log_message(self, message, version=None):
        # 检查是否是来自IPSWChecker的结果信号
        if version is not None:
            # 这是来自result_signal的结果消息
            if version:  # 找到匹配项
                ret_version, ret_url = self.handle_result_msg(message, version)
                if ret_version:
                    msg = f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 监控到可恢复的IPSW版本 【{ret_version}】, 访问此网址下载: {ret_url}"
                    self.log_text.append(f'<font color="green">{msg}</font>')
            else:  # 未找到匹配项
                self.log_text.append(f'<font color="red">{message}</font>')
        elif "找到匹配项" in message and version is None:
            # 这可能是直接调用的日志消息，包含"找到匹配项"关键字
            self.log_text.append(f'<font color="green">{message}</font>')
        elif "未找到包含" in message:
            self.log_text.append(f'<font color="red">{message}</font>')
        else:
            # 普通日志消息
            self.log_text.append(message)
        # 自动滚动到底部
        self.log_text.verticalScrollBar().setValue(self.log_text.verticalScrollBar().maximum())

    def clear_log(self):
        """清空日志显示区域"""
        self.log_text.clear()
        # self.log_message("日志已清空")

    def schedule_next_check(self, check_time, log_message=False):
        """计算到下一个检查时间的毫秒数并设置定时器"""
        current_time = QTime.currentTime()
        # 计算今天的目标时间
        target_time = check_time
        
        # 如果今天的这个时间已经过了，则设置为明天的这个时间
        if current_time >= target_time:
            # 计算到明天同一时间的毫秒数
            msecs_to_target = current_time.msecsTo(target_time) + 24 * 60 * 60 * 1000
        else:
            # 计算到今天这个时间的毫秒数
            msecs_to_target = current_time.msecsTo(target_time)
        
        # 设置定时器
        self.timer.setSingleShot(True)  # 只触发一次
        self.timer.setInterval(msecs_to_target)
        self.timer.start()
        
        # 在日志中显示下次检查时间
        if log_message:
            next_check = datetime.datetime.now() + datetime.timedelta(milliseconds=msecs_to_target)
            self.log_message(f"下次监控时间: {next_check.strftime('%Y-%m-%d %H:%M:%S')}")

    def start_monitoring(self):
        url = self.url_input.text().strip()
        search_value = self.search_input.text().strip()
        check_time = self.time_input.time()

        if not url:
            self.log_message("错误: URL不能为空")
            return

        if not search_value:
            self.log_message("错误: 版本关键词不能为空")
            return

        # 保存配置
        self.save_config()

        # 更新按钮状态
        self.start_button.setEnabled(False)
        self.stop_button.setEnabled(True)
        self.url_input.setEnabled(False)
        self.search_input.setEnabled(False)
        self.time_input.setEnabled(False)
        self.windows_notification_radio.setEnabled(False)
        self.dingtalk_notification_radio.setEnabled(False)
        self.none_notification_radio.setEnabled(False)
        self.dingtalk_webhook_input.setEnabled(False)
        self.dingtalk_secret_input.setEnabled(False)

        self.log_message(f"开始监控 {url}，版本关键词: '{search_value}'")

        # 立即执行第一次检查
        self.start_check()

        # 计算到下一个检查时间的毫秒数
        # self.schedule_next_check(check_time)

    def start_check(self):
        url = self.url_input.text().strip()
        search_value = self.search_input.text().strip()

        self.log_message(f"[{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] 开始监控...")

        # 创建并启动检查线程
        self.checker_thread = IPSWChecker(url, search_value)
        # 只连接错误信号到日志显示
        self.checker_thread.error_signal.connect(self.log_message)
        self.checker_thread.finished.connect(self.on_check_finished)
        
        # 连接结果信号到专门的处理函数
        self.checker_thread.result_signal.connect(self.handle_result_and_log)
        
        self.checker_thread.start()

    def handle_result_and_log(self, message, version):
        """处理检查结果并记录日志"""
        # 先记录日志
        self.log_message(message, version)
        # 再处理搜索结果（发送通知等）
        self.handle_search_result(message, version)

    def handle_result_msg(self, message, version):
        """处理搜索结果并整理版本号和URL"""
        ret_version = None
        ret_url = None
        if version:
            import re
            match = re.search(r'\(([^\)]+)\)', version)
            if match:
                url = self.url_input.text().strip()
                url = url.replace("https://ipsw.me/", "https://ipsw.me/download/")
                short_version = match.group(1)
                ret_url = f"{url}/{short_version}"
            else:
                ret_url = self.url_input.text().strip()
        ret_version = version
        return ret_version, ret_url

    def handle_search_result(self, message, version):
        """处理搜索结果并发送通知"""
        # 检查消息是否包含找到的结果
        ret_version, ret_url = self.handle_result_msg(message, version)
        if ret_version:
            self.current_check_url = ret_url
            msg = f"监控到可恢复的IPSW版本 【{version}】, 点击前往下载 {self.current_check_url}"
            # 发送通知
            self.sendMsg(msg)

    def sendMsg(self, msg):
        # 根据用户选择发送不同类型的通知
        if self.windows_notification_radio.isChecked():
            # windows 系统发送通知
            if self.tray_icon:
                self.tray_icon.showMessage(
                    "IPSW Monitor",  # 标题
                    msg,         # 消息内容
                    QSystemTrayIcon.Information,  # 图标类型
                    3000             # 显示时间（毫秒）
                )
        elif self.dingtalk_notification_radio.isChecked():
            # 发送钉钉通知
            from devleo.dingtalk.dingtalkrobot import DingTalkRobot
            webhook = self.dingtalk_webhook_input.text().strip()
            secret = self.dingtalk_secret_input.text().strip()
            
            # 检查webhook和secret是否为空
            if not webhook or not secret:
                self.log_message("错误: 钉钉通知的Webhook和Secret不能为空")
                return
            
            try:
                dingtalk = DingTalkRobot(webhook, secret)
                dingtalk.send_text(msg)
            except Exception as e:
                self.log_message(f"发送钉钉通知失败: {str(e)}")
        # 如果选择"不发送通知"，则不执行任何操作

    def on_check_finished(self):
        # 检查完成后重新调度下一次检查
        check_time = self.time_input.time()
        self.schedule_next_check(check_time, log_message=True)

    def stop_monitoring(self):
        # 停止定时器
        self.timer.stop()

        # 更新按钮状态
        self.start_button.setEnabled(True)
        self.stop_button.setEnabled(False)
        self.url_input.setEnabled(True)
        self.search_input.setEnabled(True)
        self.time_input.setEnabled(True)
        self.windows_notification_radio.setEnabled(True)
        self.dingtalk_notification_radio.setEnabled(True)
        self.none_notification_radio.setEnabled(True)
        if self.dingtalk_notification_radio.isChecked():
            self.dingtalk_webhook_input.setEnabled(True)
            self.dingtalk_secret_input.setEnabled(True)
        else:
            self.dingtalk_webhook_input.setEnabled(False)
            self.dingtalk_secret_input.setEnabled(False)

        self.log_message("已停止监控")

    def toggle_dingtalk_config(self):
        """根据通知类型选择切换钉钉配置的启用状态"""
        # 只有在选择钉钉通知时才启用配置输入框
        enabled = self.dingtalk_notification_radio.isChecked()
        self.dingtalk_webhook_input.setEnabled(enabled)
        self.dingtalk_secret_input.setEnabled(enabled)
        
        # 如果选择其他通知类型，清除钉钉配置内容
        if not enabled:
            self.dingtalk_webhook_input.clear()
            self.dingtalk_secret_input.clear()

    def load_config(self):
        """加载配置文件"""
        try:
            # 加载URL
            url = self.config.get("monitor.url")
            if url:
                self.url_input.setText(url)
            
            # 加载版本关键词
            search_value = self.config.get("monitor.search_value")
            if search_value:
                self.search_input.setText(search_value)
            
            # 加载监控时间
            check_time_str = self.config.get("monitor.check_time")
            if check_time_str:
                time_obj = QTime.fromString(check_time_str, "HH:mm")
                if time_obj.isValid():
                    self.time_input.setTime(time_obj)
            
            # 加载通知类型
            notification_type = self.config.get("notification.type", "windows")
            if notification_type == "windows":
                self.windows_notification_radio.setChecked(True)
            elif notification_type == "dingtalk":
                self.dingtalk_notification_radio.setChecked(True)
            elif notification_type == "none":
                self.none_notification_radio.setChecked(True)
            
            # 加载钉钉配置
            dingtalk_webhook = self.config.get("notification.dingtalk.webhook")
            if dingtalk_webhook:
                self.dingtalk_webhook_input.setText(dingtalk_webhook)
            
            dingtalk_secret = self.config.get("notification.dingtalk.secret")
            if dingtalk_secret:
                self.dingtalk_secret_input.setText(dingtalk_secret)
                
        except Exception as e:
            self.log_message(f"加载配置时发生错误: {str(e)}")

    def save_config(self):
        """保存配置到文件"""
        try:
            # 获取当前输入的值
            url = self.url_input.text().strip()
            search_value = self.search_input.text().strip()
            check_time = self.time_input.time().toString("HH:mm")
            dingtalk_webhook = self.dingtalk_webhook_input.text().strip()
            dingtalk_secret = self.dingtalk_secret_input.text().strip()
            
            # 确定通知类型
            if self.windows_notification_radio.isChecked():
                notification_type = "windows"
            elif self.dingtalk_notification_radio.isChecked():
                notification_type = "dingtalk"
            else:
                notification_type = "none"
            
            # 构建配置数据
            config_data = {
                "monitor": {
                    "url": url,
                    "search_value": search_value,
                    "check_time": check_time
                },
                "notification": {
                    "type": notification_type,
                    "dingtalk": {
                        "webhook": dingtalk_webhook,
                        "secret": dingtalk_secret
                    }
                }
            }
            
            # 保存配置
            self.config.save(config_data)
            self.log_message("配置已保存")
        except Exception as e:
            self.log_message(f"保存配置时发生错误: {str(e)}")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    
    # 尝试连接到已存在的实例
    socket = QLocalSocket()
    socket.connectToServer("IPSWMonitorUniqueID")
    
    if socket.waitForConnected(1000):
        # 如果连接成功，说明已经有实例在运行
        # 发送消息通知旧实例显示窗口
        socket.write(b"SHOW")
        socket.flush()
        socket.disconnectFromServer()
        # 退出当前实例
        sys.exit(0)
    
    # 如果连接失败，说明没有实例在运行，正常启动
    window = IPSWMonitorGUI()
    window.show()
    sys.exit(app.exec_())
